/**
 * Implementacion de la clase EdgeValidator.
 */

#include "edgeValidator.hpp"
#include <stdlib.h>
#include <sstream>

using namespace std;

EdgeValidator::~EdgeValidator() {

}

void EdgeValidator::create(std::string &fileToParse, std::string &fileConf, Logger *log, int numberOfFields, Graph *graph)
{
	this->parser.create(fileToParse,fileConf,log);
	this->logger = log;
	this->numberOfFieldsExpected = numberOfFields;
	this->graph = graph;
	this->edge.setSuccessStatus(6);
}

void EdgeValidator::validate()
{
	std::string field;
	int lineCount 	= 1;
	int fieldAmount = 0;

	while(this->parser.nextLine())
	{
		fieldAmount = this->parser.getNumberOfFields();
		this->validateNumberOfFields(lineCount,fieldAmount);

		for(int fieldNumber = 1; fieldNumber <= fieldAmount; fieldNumber++)
		{
			this->parser.getField(fieldNumber,field);
			this->validateField(lineCount,fieldNumber,field);
			field.clear();
		}
		this->validateGraphElementAdding(lineCount);
		lineCount++;
	}
}

void EdgeValidator::validateNumberOfFields(int lineCount, int fieldAmount)
{
	if(fieldAmount != this->numberOfFieldsExpected)
	{
		this->edge.setFailStatus(6);
		std::string detail = "Cantidad de campos distinta a la esperada";
		this->logger->reportErrorMessage("EdgeValidator","validateNumberOfFields", detail, lineCount);
	}
	else
		this->edge.setSuccessStatus(6);

}

void EdgeValidator::validateField(int lineCount, int fieldNumber, std::string field)
{
	std::string detail;
	if(fieldNumber <= 3)
	{
		if((std::string::npos == field.find_first_of(','))&&(std::string::npos == field.find_first_of('.'))
			&&(field[0] != '0')&&(atoi(field.c_str()) == 0))
		{
			if(fieldNumber == 1)
				this->edge.setEdgeId(field);
			if(fieldNumber == 2)
				this->edge.setNodeOriginId(field);
			if(fieldNumber == 3)
				this->edge.setNodeDestinyId(field);
		}
		else
		{
			detail = "Esperado un literal, se encontro un real";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
	}
	else
	{
		float numero = atof(field.c_str());
		if(numero > 0)
		{
			if(fieldNumber == 4)
				this->edge.setLongitude(numero);
			else
				this->edge.setVelocity(numero);
		}
		else if (numero == 0)
		{
			detail = "Esperado un real, se encontro un literal, o el real vale cero";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
		else if(numero < 0)
		{
			detail = "El valor es negativo";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
	}
}

void EdgeValidator::validateGraphElementAdding(int lineCount)
{
	bool success = true;
	std::string detail;

	if(this->edge.getEdgeIdStatus() == 0)
		success = false;
	if(this->edge.getNodeOriginIdStatus() == 0)
		success = false;
	if(this->edge.getNodeDestinyIdStatus() == 0)
		success = false;
	if(this->edge.getLongitudeStatus() == 0)
		success = false;
	if(this->edge.getVelocityStatus() == 0)
		success = false;
	if(this->edge.getNumberOfFieldsStatus() == 0)
		success = false;

	if(success)
	{
		// TODO Nico: optimizar el siguiente codigo en lo posible
		Node *nodeOrigin = this->graph->getNode(this->edge.getNodeOriginId());
		Node *nodeDestiny = this->graph->getNode(this->edge.getNodeDestinyId());
		if((nodeOrigin != NULL)&&(nodeDestiny != NULL))
		{
			Edge *tempEgde = this->graph->getEdge(this->edge.getEdgeId(),this->edge.getNodeOriginId());
			if(tempEgde == NULL)
			{
				Weight w(this->edge.getVelocity(),this->edge.getLongitude());
				Edge *newEdge = new Edge(this->edge.getEdgeId(),this->edge.getNodeOriginId(),
								 this->edge.getNodeDestinyId(), w);
				this->graph->addEdge(newEdge);

				detail = "Arista agregada, ";
				detail += this->edge.getEdgeId();
				this->logger->reportSuccessMessage("EdgeValidator","validateGraphElementAdding",detail);

			}
			else
			{
				detail = "Arista con Id existente descartada, id: "; detail += this->edge.getEdgeId();
				this->logger->reportErrorMessage("EdgeValidator","validateGraphElementAdding",detail,lineCount);

			}
		}
		else if(nodeOrigin == NULL)
		{
			detail = "Id de nodo origen de arista inexistente, arista descartada, id: "; detail += this->edge.getEdgeId();
			this->logger->reportErrorMessage("EdgeValidator","validateGraphElementAdding",detail,lineCount);
		}
		else if(nodeDestiny == NULL)
		{
			detail = "Id de nodo destino de arista inexistente, arista descartada, id: "; detail += this->edge.getEdgeId();
			this->logger->reportErrorMessage("EdgeValidator","validateGraphElementAdding",detail,lineCount);
		}

	}
	else
	{
		std::string detail = "Arista descartada";
		this->logger->reportErrorMessage("EdgeValidator","validateGraphElementAdding",detail);
		this->edge.clear();

	}
}
